package pap.test.fcPuGm;

import gov.nasa.jpf.jvm.Verify;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;

public class PositionUpdater extends Task {

	BufferManager bufferManager;
	volatile int updatedBodies = 0;
	List<Body> bodiesList;
	LinkedBlockingDeque<Body> bodiesToUpdate;
	static AtomicInteger iterCounter;
	int iterations;
	SynchronizedCounter ccToFcCounter;

	public PositionUpdater(int threadNum,
			LinkedBlockingDeque<Body> bodiesToUpdate, List<Body> bodiesList,
			BufferManager bufferManager, int iterations,
			SynchronizedCounter ccToFcCounter) {
		super(threadNum);

		Verify.beginAtomic();
		this.bodiesList = bodiesList;
		this.bodiesToUpdate = bodiesToUpdate;
		this.bufferManager = bufferManager;
		this.iterCounter = new AtomicInteger(0);
		this.iterations = iterations;
		this.ccToFcCounter = ccToFcCounter;
		Verify.endAtomic();
	}

	@Override
	protected void mainTask() {
		Body body = null;

		while (iterCounter.getAndIncrement() < iterations) {
			try {
				body = bodiesToUpdate.takeFirst();

				updateBodyPosition(body);

				increaseUpdatedBodiesNumber();

			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

	/*
	 * Every time a body is updated is necessary for the executor to notify the
	 * completion. The value of the counter 'updatedBodies' is incremented by
	 * one, allowing to know when all bodies have been updated. After that is
	 * necessary to wake the component CollisionChecker.
	 */
	private synchronized void increaseUpdatedBodiesNumber()
			throws InterruptedException {
		updatedBodies++;

		/*
		 * If all bodies have been updated, the counter must be reset, since
		 * every executor will be blocked on the queue while a full step will be
		 * completed
		 */
		if (updatedBodies >= bodiesList.size()) {
			updatedBodies = 0;

			// Can't wake up the CollisionChecker until the buffer is full
			bufferManager.startNewStep();

			wakePositionUpdater();
		}

	}

	private void wakePositionUpdater() {
		ccToFcCounter.reset(bodiesList.size());
	}

	/**
	 * Updates the body's position and adds it to the buffer.
	 * 
	 * @param body
	 * @throws UncompatibleDimensionsException
	 */
	private void updateBodyPosition(Body body) {
		body.updateAndStorePosition();

		if (body instanceof SimpleBody) {
			bufferManager.addBody((SimpleBody) body);
		} else {
			CompoundBody compoundBody = (CompoundBody) body;
			for (SimpleBody componentBody : compoundBody.getBodies()) {
				bufferManager.addBody(componentBody);
			}

		}

	}

}